sqlrite-engine 0.10.0

Light version of SQLite developed with Rust. Published as `sqlrite-engine` on crates.io; import as `use sqlrite::…`.
Documentation
import Link from "next/link";
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { Footer } from "@/components/footer";
import { Nav } from "@/components/nav";
import { BlogMDX } from "@/components/blog-mdx";
import { SITE } from "@/lib/site";
import {
  formatDate,
  getAllPostSlugs,
  getAllPosts,
  getPostBySlug,
  tagToSlug,
} from "@/lib/blog";

type RouteParams = { params: Promise<{ slug: string }> };

export function generateStaticParams() {
  return getAllPostSlugs().map((slug) => ({ slug }));
}

export async function generateMetadata({
  params,
}: RouteParams): Promise<Metadata> {
  const { slug } = await params;
  const post = getPostBySlug(slug);
  if (!post) return {};

  const url = `${SITE.url}/blog/${post.slug}`;
  return {
    title: post.frontmatter.title,
    description: post.frontmatter.description,
    authors: [{ name: post.frontmatter.author }],
    keywords: post.frontmatter.tags,
    alternates: { canonical: `/blog/${post.slug}` },
    openGraph: {
      type: "article",
      siteName: "SQLRite",
      locale: "en_US",
      url,
      title: post.frontmatter.title,
      description: post.frontmatter.description,
      publishedTime: post.frontmatter.publishedAt,
      modifiedTime: post.frontmatter.updatedAt,
      authors: [post.frontmatter.author],
      tags: post.frontmatter.tags,
    },
    twitter: {
      card: "summary_large_image",
      site: SITE.twitterHandle,
      creator: SITE.twitterHandle,
      title: post.frontmatter.title,
      description: post.frontmatter.description,
    },
  };
}

export default async function BlogPostPage({ params }: RouteParams) {
  const { slug } = await params;
  const post = getPostBySlug(slug);
  if (!post) notFound();

  const url = `${SITE.url}/blog/${post.slug}`;
  const allPosts = getAllPosts();
  const idx = allPosts.findIndex((p) => p.slug === post.slug);
  // Posts sort newest-first, so idx-1 is newer and idx+1 is older.
  const newer = idx > 0 ? allPosts[idx - 1] : null;
  const older =
    idx >= 0 && idx < allPosts.length - 1 ? allPosts[idx + 1] : null;

  const articleJsonLd = {
    "@context": "https://schema.org",
    "@type": "BlogPosting",
    headline: post.frontmatter.title,
    description: post.frontmatter.description,
    datePublished: post.frontmatter.publishedAt,
    dateModified: post.frontmatter.updatedAt ?? post.frontmatter.publishedAt,
    author: {
      "@type": "Person",
      name: post.frontmatter.author,
      url: SITE.socials.github,
    },
    publisher: {
      "@type": "Organization",
      name: "SQLRite",
      url: SITE.url,
    },
    mainEntityOfPage: {
      "@type": "WebPage",
      "@id": url,
    },
    url,
    image: `${url}/opengraph-image`,
    keywords: post.frontmatter.tags.join(", "),
  };

  const breadcrumbJsonLd = {
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    itemListElement: [
      { "@type": "ListItem", position: 1, name: "Home", item: SITE.url },
      {
        "@type": "ListItem",
        position: 2,
        name: "Blog",
        item: `${SITE.url}/blog`,
      },
      {
        "@type": "ListItem",
        position: 3,
        name: post.frontmatter.title,
        item: url,
      },
    ],
  };

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
      />
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }}
      />
      <Nav variant="docs" />

      <article className="blog-article">
        <header className="blog-article-head">
          <nav className="blog-crumbs" aria-label="Breadcrumb">
            <Link href="/blog">← All posts</Link>
          </nav>
          <h1 className="blog-article-title">{post.frontmatter.title}</h1>
          <p className="blog-article-lede">{post.frontmatter.description}</p>
          <div className="blog-article-meta">
            <span>
              By{" "}
              <a
                href={SITE.socials.github}
                target="_blank"
                rel="noreferrer"
                style={{ color: "var(--color-fg)" }}
              >
                {post.frontmatter.author}
              </a>
            </span>
            <span aria-hidden>·</span>
            <time dateTime={post.frontmatter.publishedAt}>
              {formatDate(post.frontmatter.publishedAt)}
            </time>
            <span aria-hidden>·</span>
            <span>{post.readingTime} min read</span>
          </div>
          <div className="blog-article-tags">
            {post.frontmatter.tags.map((t) => (
              <Link
                key={t}
                href={`/blog/tags/${tagToSlug(t)}`}
                className="blog-tag"
              >
                {t}
              </Link>
            ))}
          </div>
        </header>

        <div className="blog-article-body">
          <BlogMDX source={post.content} />
        </div>

        <footer className="blog-article-foot">
          <div className="blog-cta-card">
            <h3>Liked this post?</h3>
            <p>
              SQLRite is open source. Star it on GitHub, install via{" "}
              <code>cargo install sqlrite-engine</code>, or read the{" "}
              <Link href="/docs">docs</Link>.
            </p>
            <div className="cta-row">
              <a className="btn btn-primary" href={SITE.repo}>
                Star on GitHub
              </a>
              <Link className="btn" href="/docs">
                Read the docs
              </Link>
              <Link className="btn" href="/blog">
                More posts
              </Link>
            </div>
          </div>

          {(older || newer) && (
            <nav className="blog-pager" aria-label="Post navigation">
              {older ? (
                <Link
                  href={`/blog/${older.slug}`}
                  className="blog-pager-link"
                >
                  <span className="blog-pager-label">
                    <span aria-hidden>← </span>Older
                  </span>
                  <span className="blog-pager-title">
                    {older.frontmatter.title}
                  </span>
                </Link>
              ) : (
                <span />
              )}
              {newer ? (
                <Link
                  href={`/blog/${newer.slug}`}
                  className="blog-pager-link blog-pager-link-right"
                >
                  <span className="blog-pager-label">
                    Newer<span aria-hidden> →</span>
                  </span>
                  <span className="blog-pager-title">
                    {newer.frontmatter.title}
                  </span>
                </Link>
              ) : (
                <span />
              )}
            </nav>
          )}
        </footer>
      </article>
      <Footer />
    </>
  );
}